using System;
using System.Collections.Generic;
using UnityEngine;

public class HitParamsDB : ScriptableObjectSingleton<HitParamsDB>
{
	public enum TimingRangeModifierType
	{
		None = 0,
		Green = 1,
		Yellow = 2
	}

	[Serializable]
	public class TimingRangeModifier
	{
		public TimingRangeModifierType type;

		public float reductionPerSkillDifferencePoint;

		public float bonusPerSkillDifferencePoint;

		public bool hasMinToMaxFactor;

		public float minToMaxFactor;

		public float Modify(float value, float skillDifference)
		{
			float num = Mathf.Abs(skillDifference);
			value = ((!(skillDifference > 0f)) ? (value / (1f + num * reductionPerSkillDifferencePoint)) : (value * (1f + num * bonusPerSkillDifferencePoint)));
			return value;
		}

		public float ApplyMinToMaxFactorConstraint(float first, float second)
		{
			if (!hasMinToMaxFactor)
			{
				return first;
			}
			return Mathf.Sign(first) * Mathf.Min(Mathf.Abs(first), Mathf.Abs(second) * 1.2f);
		}
	}

	[Serializable]
	public class TimingRange
	{
		public string name;

		public FloatRange timingRange;

		public TimingRangeModifierType minModifierType;

		public TimingRangeModifierType maxModifierType;

		public FloatRange quality;

		public Color color;

		public Color colorTimer;

		public string message;

		public float chanceToMissShotPercent;

		public float heightAdd;

		public FloatRange GetSkillDifferenceModifiedTimingRange(float skillDifference)
		{
			FloatRange result = default(FloatRange);
			TimingRangeModifier modifier = ScriptableObjectSingleton<HitParamsDB>.instance.GetModifier(minModifierType);
			TimingRangeModifier modifier2 = ScriptableObjectSingleton<HitParamsDB>.instance.GetModifier(maxModifierType);
			float num = timingRange.min;
			float num2 = timingRange.max;
			if (modifier != null)
			{
				num = modifier.Modify(num, skillDifference);
			}
			if (modifier2 != null)
			{
				num2 = modifier2.Modify(num2, skillDifference);
			}
			if (modifier != null)
			{
				num = modifier.ApplyMinToMaxFactorConstraint(num, num2);
			}
			if (modifier2 != null)
			{
				num2 = modifier2.ApplyMinToMaxFactorConstraint(num2, num);
			}
			result.min = Mathf.Clamp(num, -1f, 1f);
			result.max = Mathf.Clamp(num2, -1f, 1f);
			return result;
		}

		public bool isAcceptable(float normTiming, float skillDifference)
		{
			FloatRange skillDifferenceModifiedTimingRange = GetSkillDifferenceModifiedTimingRange(skillDifference);
			return normTiming >= Mathf.Min(skillDifferenceModifiedTimingRange.min, skillDifferenceModifiedTimingRange.max) && normTiming <= Mathf.Max(skillDifferenceModifiedTimingRange.min, skillDifferenceModifiedTimingRange.max);
		}

		public float GetQuality(float normTiming, float skillDifference)
		{
			FloatRange skillDifferenceModifiedTimingRange = GetSkillDifferenceModifiedTimingRange(skillDifference);
			return quality.Lerp(skillDifferenceModifiedTimingRange.InverseLerp(normTiming));
		}
	}

	[Serializable]
	public class Timing
	{
		public float maxTimeMult = 1f;

		public FloatRange timingInSecs;

		public List<TimingRange> ranges = new List<TimingRange>();

		public FloatRange GetRealRangeInSec(float maxTiming)
		{
			FloatRange result = timingInSecs;
			result.min = Mathf.Sign(timingInSecs.min) * Mathf.Min(Mathf.Abs(maxTiming * maxTimeMult), Mathf.Abs(timingInSecs.min));
			result.max = Mathf.Sign(timingInSecs.max) * Mathf.Min(Mathf.Abs(maxTiming * maxTimeMult), Mathf.Abs(timingInSecs.max));
			return result;
		}

		public float GetNormTiming(float timing, float maxTiming)
		{
			return GetRealRangeInSec(maxTiming).InverseLerpZeroCenter(timing);
		}

		public TimingRange GetTimingRange(float timing, float skillDifference, float maxTiming)
		{
			float normTiming = GetNormTiming(timing, maxTiming);
			foreach (TimingRange range in ranges)
			{
				if (range.isAcceptable(normTiming, skillDifference))
				{
					return range;
				}
			}
			return ranges[ranges.Count - 1];
		}
	}

	public enum HitType
	{
		TopspinHit = 0,
		TopspinBackhand = 1,
		SaveHit = 2,
		Backspin = 3,
		BackspinSave = 4,
		Smash = 5,
		Serve = 6
	}

	public struct HitParams
	{
		public float strength;

		public float heightAboveNet;

		public float spin;

		public float xRange;

		public float xRange2;

		public bool shouldMissShot;

		public float skillPointsReductionFactor;

		public float opponentDefensePoints;

		public float xRandomRange;

		public float returnSpeed;

		public float returnZAdjust;

		public float returnXAdjust;

		public bool canMissSide;

		public QualityRange range;

		public float racketInfluenceGreen;

		public float racketInfluenceYellow;
	}

	[Serializable]
	public class QualityRange
	{
		public FloatRange qualityRange;

		public FloatRange heightAboveNet;

		public FloatRange spin;

		public FloatRange missShotRange;

		public bool canMissSide = true;

		public FloatRange xRange = new FloatRange
		{
			min = -1f,
			max = 1f
		};

		public FloatRange xRange2 = new FloatRange
		{
			min = 1f,
			max = 1f
		};

		public FloatRange xRandomRange = default(FloatRange);

		public FloatRange strength = new FloatRange
		{
			min = -0.5f,
			max = 1f
		};

		public FloatRange skillPointsReductionFactor = new FloatRange
		{
			min = 1f,
			max = 1f
		};

		public FloatRange opponentDefensePoints;

		public FloatRange returnSpeed;

		public FloatRange returnZAdjust;

		public FloatRange returnXAdjust;

		public FloatRange shotsTillMiss;

		public float racketInfluenceGreen;

		public float racketInfluenceYellow;

		public bool canGoHigh;

		public FloatRange chanceToGoHigh;

		public float highHeight = 1.1f;

		public List<AudioClip> hitClips = new List<AudioClip>();

		public AudioClip GetClip()
		{
			if (hitClips.Count == 0)
			{
				return null;
			}
			return hitClips[UnityEngine.Random.Range(0, hitClips.Count) % hitClips.Count];
		}

		public bool isAcceptable(float quality)
		{
			return quality >= Mathf.Min(qualityRange.min, qualityRange.max) && quality <= Mathf.Max(qualityRange.min, qualityRange.max);
		}

		public float GetNormQuality(float quality)
		{
			return qualityRange.InverseLerp(quality);
		}
	}

	[Serializable]
	public class HitRange
	{
		public string name;

		public FloatRange timingRange;

		public float addDefenseSkillPoints;

		public List<QualityRange> ranges;

		public QualityRange GetQualityRange(float quality)
		{
			foreach (QualityRange range in ranges)
			{
				if (range.isAcceptable(quality))
				{
					return range;
				}
			}
			return ranges[ranges.Count - 1];
		}

		public bool isAcceptableForTiming(float normTiming)
		{
			return normTiming >= Mathf.Min(timingRange.min, timingRange.max) && normTiming <= Mathf.Max(timingRange.min, timingRange.max);
		}

		public HitParams GetHitParams(float normTiming, float quality, int shotsInPoint)
		{
			HitParams result = default(HitParams);
			float value = timingRange.InverseLerp(normTiming);
			QualityRange qualityRange = GetQualityRange(quality);
			float normQuality = qualityRange.GetNormQuality(quality);
			result.range = qualityRange;
			bool flag = GaussDistribution.instance.getDecissionWithSuccess(qualityRange.missShotRange.Lerp(normQuality) / 100f);
			result.strength = qualityRange.strength.Lerp(value);
			result.heightAboveNet = qualityRange.heightAboveNet.Lerp(normQuality);
			result.spin = qualityRange.spin.Lerp(value);
			result.xRange = qualityRange.xRange.Lerp(normQuality);
			result.xRange2 = qualityRange.xRange2.Lerp(normQuality);
			result.skillPointsReductionFactor = qualityRange.skillPointsReductionFactor.Lerp(normQuality);
			result.canMissSide = qualityRange.canMissSide;
			result.opponentDefensePoints = qualityRange.opponentDefensePoints.Lerp(normQuality);
			float num = qualityRange.xRandomRange.Lerp(normQuality) * Table.halfwidth;
			result.xRandomRange = (float)((!GaussDistribution.instance.getDecissionWithSuccess(0.5f)) ? 1 : (-1)) * Mathf.Min(Mathf.Abs(GaussDistribution.instance.Next(0f, Mathf.Abs(num))), num);
			result.returnSpeed = qualityRange.returnSpeed.Lerp(normQuality);
			result.returnZAdjust = qualityRange.returnZAdjust.Lerp(normQuality);
			result.returnXAdjust = qualityRange.returnXAdjust.Lerp(normQuality);
			result.racketInfluenceGreen = qualityRange.racketInfluenceGreen;
			result.racketInfluenceYellow = qualityRange.racketInfluenceYellow;
			if ((float)shotsInPoint < qualityRange.shotsTillMiss.Lerp(normQuality))
			{
				flag = false;
			}
			if (qualityRange.canGoHigh && !flag && GaussDistribution.instance.getDecissionWithSuccess(qualityRange.chanceToGoHigh.Lerp(normQuality) / 100f))
			{
				result.heightAboveNet = qualityRange.highHeight;
			}
			if (flag)
			{
				result.strength = 1.3f;
			}
			result.shouldMissShot = flag;
			return result;
		}
	}

	[Serializable]
	public class Hit
	{
		public string name;

		public HitType type;

		public PlayerStatsManager.SkillType skillType;

		public FloatRange timingInSecs = default(FloatRange);

		public List<HitRange> ranges = new List<HitRange>();

		public HitRange GetRange(float normTiming)
		{
			foreach (HitRange range in ranges)
			{
				if (range.isAcceptableForTiming(normTiming))
				{
					return range;
				}
			}
			return ranges[ranges.Count - 1];
		}

		public float GetNormTiming(float timing, FlightSequence.FlightPlanStage stage)
		{
			FloatRange floatRange = default(FloatRange);
			floatRange.min = timingInSecs.min;
			floatRange.max = timingInSecs.max;
			if (stage.startTime * 0.8f < Mathf.Abs(timingInSecs.min))
			{
				floatRange.min = (0f - stage.startTime) * 0.8f;
			}
			if (stage.duration * 0.5f < Mathf.Abs(timingInSecs.max))
			{
				floatRange.max = stage.duration * 0.5f;
			}
			return floatRange.InverseLerpZeroCenter(timing);
		}

		public HitParams GetHitParams(float timing, float quality, FlightSequence.FlightPlanStage stage, int shotsInPoint)
		{
			HitRange range = GetRange(timing);
			return range.GetHitParams(timing, quality, shotsInPoint);
		}
	}

	public List<TimingRangeModifier> timingModifiers = new List<TimingRangeModifier>();

	public Timing timing = new Timing();

	public List<Hit> hits = new List<Hit>();

	public TimingRangeModifier GetModifier(TimingRangeModifierType type)
	{
		foreach (TimingRangeModifier timingModifier in timingModifiers)
		{
			if (timingModifier.type == type)
			{
				return timingModifier;
			}
		}
		return null;
	}

	public Hit GetHit(HitType hitType)
	{
		foreach (Hit hit in hits)
		{
			if (hit.type == hitType)
			{
				return hit;
			}
		}
		return null;
	}

	protected override void Init()
	{
		foreach (Hit hit in hits)
		{
			hit.name = hit.type.ToString();
		}
	}
}
